﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using Newtonsoft.Json.Linq;

namespace Noria.Web.MyMSDN
{
    internal static class HttpBot
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private const int BlockSize = 16 * 1024; // 16K

        public static void RequestNonQuery(IStatistics stat, string url, byte[] body = null, string contentType = null, IDictionary<String, String> headers = null)
        {
            RequestBinary(stat, url, body, contentType, headers);
        }

        public static byte[] RequestBinary(IStatistics stat, string url, byte[] body = null, string contentType = null, IDictionary<String, String> headers = null)
        {
            Stopwatch watch = null;
            long latency = 0;

            if (stat != null)
            {
                watch = new Stopwatch();
                watch.Start();
            }

            var request = CreateRequest(url);

            if (headers != null)
            {
                foreach (var key in headers.Keys)
                {
                    request.Headers.Add(key, headers[key]);
                }
            }

            if (body != null)
            {
                request.Method = WebRequestMethods.Http.Post;

                if (contentType != null)
                {
                    request.ContentType = contentType;
                }

                var buffer = body;
                request.GetRequestStream().Write(buffer, 0, buffer.Length);
            }

            using (var response = request.GetResponse())
            {
                if (stat != null)
                {
                    watch.Stop();
                    latency = watch.ElapsedMilliseconds;
                }

                // weird, I never get correct information from content length and encoding...
                //
                // HttpWebResponse.ContentEncoding always returns an empty string
                // HttpWebResponse.ContentLength sometimes returns -1 which doesn't make any sense
                //
                log.DebugFormat("Request {0}, length={1}, encoding={2}",
                    url, response.ContentLength, ((HttpWebResponse)response).ContentEncoding);

                // weird again, response.Headers["Content-Encoding"] always returns an empty string...
                //
                //foreach (string key in response.Headers.Keys)
                //{
                //    log.DebugFormat(" {0}: {1}", key, response.Headers[key]);
                //}

                var stream = response.GetResponseStream();
                var buffer = ReadFully(stream);

                if (stat != null)
                {
                    long payload = buffer.Length;
                    stat.AddHttp(latency, payload);
                }

                return buffer;
            }
        }

        public static string RequestText(IStatistics stat, string url, Encoding encoding = null, byte[] body = null, string contentType = null, IDictionary<String, String> headers = null)
        {
            if (encoding == null)
                encoding = Encoding.UTF8;

            var buffer = RequestBinary(stat, url, body, contentType, headers);
            var text = encoding.GetString(buffer);

            return text;
        }

        public static JObject RequestJObject(IStatistics stat, string url, byte[] body = null, string contentType = null, IDictionary<String, String> headers = null)
        {
            var text = RequestText(stat, url, null, body, contentType, headers);

            try
            {
                var json = JObject.Parse(text);
                return json;
            }
            catch
            {
                log.Error("Failed to parse text: " + text);
                throw;
            }
        }

        public static JArray RequestJArray(IStatistics stat, string url, byte[] body = null, string contentType = null, IDictionary<String, String> headers = null)
        {
            var text = RequestText(stat, url, null, body, contentType, headers);

            try
            {
                var json = JArray.Parse(text);
                return json;
            }
            catch
            {
                log.Error("Failed to parse text: " + text);
                throw;
            }
        }

        private static HttpWebRequest CreateRequest(string url)
        {
            var request = WebRequest.CreateHttp(url);
            request.AllowAutoRedirect = false;
            request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

            return request;
        }

        private static byte[] ReadFully(Stream input)
        {
            var buffer = new byte[BlockSize];
            using (var ms = new MemoryStream())
            {
                int read;
                while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                return ms.ToArray();
            }
        }

    }
}